"""
doc
"""

import json
import logging
from datetime import timedelta

from django.views import View
from django.http import HttpRequest
from django.db import transaction
from django.utils.decorators import method_decorator
from django.utils import timezone

from rest_framework.parsers import JSONParser

from app_common import (
    app_page,
    app_except,
    app_str_check,
    json_response_utf8_ok,
)

from app_community import models as community_models

from app_community import ser as community_ser

from . import models, ser, wx_auth, wx_api, wx_pay

logger = logging.getLogger("log")


# Create your views here.
@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="get")
@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="put")
@method_decorator(wx_auth.try_decorators, name="post")
class WxUserView(View):
    """doc"""

    def get(self, request: HttpRequest, *args, **kwargs):
        """doc"""
        wxuser: models.WxUser = request.wxuser
        m_s = ser.WxUserSer(wxuser)
        return json_response_utf8_ok({"data": m_s.data})

    @transaction.atomic
    def post(self, request: HttpRequest, *args, **kwargs):
        """doc"""
        openid = request.META.get("HTTP_X_WX_OPENID")
        body: dict = JSONParser().parse(request)

        app_str_check.check_name(body["nick_name"])
        app_str_check.check_url(body["head_img_url"])

        telephone = wx_api.get_phone(request, body)
        app_str_check.check_telephone(telephone)

        models.WxUser.objects.select_for_update()
        if models.WxUser.objects.filter(deleted=False).filter(openid=openid).exists():
            raise app_except.WxOpenidExist

        body.pop("cloudID")
        body["openid"] = openid
        body["telephone"] = telephone
        m = models.WxUser(**body)
        m.save()
        m_s = ser.WxUserSer(m)
        return json_response_utf8_ok({"data": m_s.data})

    @transaction.atomic
    def put(self, request: HttpRequest, *args, **kwargs):
        body: dict = JSONParser().parse(request)
        wxuser: models.WxUser = request.wxuser

        readable_attrs = ["telephone"]
        for attr in readable_attrs:
            if attr in body:
                raise app_except.OnlyReadAttr

        if body.get("nick_name", None):
            app_str_check.check_name(body["nick_name"])
        if body.get("head_img_url", None):
            app_str_check.check_url(body["head_img_url"])

        if default_community_uuid := body.get("default_community_uuid", None):
            if (
                not community_models.Community.objects.filter(deleted=False)
                .filter(uuid=default_community_uuid)
                .exists()
            ):
                raise app_except.ObjectNotExist

        if default_house_uuid := body.get("default_house_uuid", None):
            if (
                not community_models.House.objects.filter(deleted=False)
                .filter(uuid=default_house_uuid)
                .exists()
            ):
                raise app_except.ObjectNotExist

        m_s = ser.WxUserSer(wxuser, data=body, partial=True)
        m_s.is_valid(raise_exception=True)
        m_s.save()

        return json_response_utf8_ok({"data": m_s.data})


@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="dispatch")
class WxUserHouseView(View):
    """用户和房屋绑定关系"""

    def get(self, request: HttpRequest, *args, **kwargs):
        wxuser: models.WxUser = request.wxuser

        _house_uuids = (
            models.WxUserHouse.objects.filter(deleted=False)
            .filter(user_uuid=wxuser.uuid)
            .values_list("house_uuid")
        )
        house_uuids = [x[0] for x in _house_uuids]
        m = community_models.House.objects.filter(deleted=False).filter(
            uuid__in=house_uuids
        )

        m_s = community_ser.HouseSer(m, many=True)
        return json_response_utf8_ok({"data": m_s.data})

    @transaction.atomic
    def post(self, request: HttpRequest, *args, **kwargs):
        wxuser: models.WxUser = request.wxuser
        body: dict = JSONParser().parse(request)
        house_uuid = body["house_uuid"]
        if (
            not community_models.House.objects.filter(deleted=False)
            .filter(uuid=house_uuid)
            .exists()
        ):
            raise app_except.ObjectNotExist

        models.WxUserHouse.objects.select_for_update()
        _data = {
            "user_uuid": wxuser.uuid,
            "house_uuid": body["house_uuid"],
        }

        _m = (
            models.WxUserHouse.objects.filter(deleted=False)
            .filter(user_uuid=wxuser.uuid)
            .filter(house_uuid=house_uuid)
            .select_for_update()
            .first()
        )
        if _m:
            raise app_except.ObjectExist

        m = models.WxUserHouse(**_data)
        m.save()

        return json_response_utf8_ok({"data": f"绑定成功: {m.uuid}"})

    @transaction.atomic
    def delete(self, request: HttpRequest, *args, **kwargs):
        wxuser: models.WxUser = request.wxuser
        body: dict = JSONParser().parse(request)
        house_uuid = body["house_uuid"]

        n = (
            models.WxUserHouse.objects.filter(deleted=False)
            .filter(user_uuid=wxuser.uuid)
            .filter(house_uuid=house_uuid)
            .select_for_update()
            .update(deleted=True)
        )
        if n == 0:
            raise app_except.ObjectNotExist

        return json_response_utf8_ok({"data": f"解除绑定个数: {n}"})


@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="dispatch")
class WxCommunityView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        _objs = community_models.Community.objects.filter(deleted=False)
        m_s = community_ser.CommunitySer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data})


@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="dispatch")
class WxBuildingView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        community_uuid = request.GET["community_uuid"]

        _objs = (
            community_models.House.objects.filter(deleted=False)
            .filter(community_uuid=community_uuid)
            .values_list("building_num")
            .distinct()
        )
        data = [x[0] for x in _objs]
        return json_response_utf8_ok({"data": data})


@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="dispatch")
class WxUnitView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        community_uuid = request.GET["community_uuid"]
        building_num = request.GET["building_num"]

        _objs = (
            community_models.House.objects.filter(deleted=False)
            .filter(community_uuid=community_uuid)
            .filter(building_num=building_num)
            .values_list("unit_num")
            .distinct()
        )
        data = [x[0] for x in _objs]
        return json_response_utf8_ok({"data": data})


@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="dispatch")
class WxHouseView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        community_uuid = request.GET["community_uuid"]
        building_num = request.GET["building_num"]
        unit_num = request.GET["unit_num"]

        _objs = (
            community_models.House.objects.filter(deleted=False)
            .filter(community_uuid=community_uuid)
            .filter(building_num=building_num)
            .filter(unit_num=unit_num)
        )
        m_s = community_ser.HouseSer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data})


@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="dispatch")
class WxHouseBillView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        def query_by_house_uuid() -> list[community_models.HouseBill]:
            bind = (
                models.WxUserHouse.objects.filter(deleted=False)
                .filter(user_uuid=wxuser.uuid)
                .filter(house_uuid=house_uuid)
            )

            if not bind:
                raise app_except.NotBindHouse

            _objs = community_models.HouseBill.objects.filter(deleted=False).filter(
                house_uuid=house_uuid
            )
            return _objs

        def query_by_user_uuid() -> list[community_models.HouseBill]:
            _objs = community_models.HouseBill.objects.filter(deleted=False).filter(
                pay_user_openid=wxuser.openid
            )
            return _objs

        wxuser: models.WxUser = request.wxuser
        house_uuid = request.GET.get("house_uuid", None)

        _objs = None
        if house_uuid:
            _objs = query_by_house_uuid()
        else:
            _objs = query_by_user_uuid()
        _objs, num_pages = app_page.page(request, _objs)
        m_s = community_ser.HouseBillSer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data, "num_pages": num_pages})


@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="dispatch")
class WxUserRechargeOrderView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        wxuser: models.WxUser = request.wxuser
        _objs = models.WxUserRechargeOrder.objects.filter(deleted=False).filter(
            openid=wxuser.openid
        )
        _objs, num_pages = app_page.page(request, _objs)
        m_s = ser.WxUserRechargeOrderSer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data, "num_pages": num_pages})

    @transaction.atomic
    def post(self, request, *args, **kwargs):
        # Perform io-blocking view logic using await, sleep for example.
        wxuser: models.WxUser = request.wxuser
        o: dict = json.loads(request.body)
        house_uuid = o["house_uuid"]
        days = int(o["days"])
        if days < 1 or days > 3650:
            raise app_except.ValueErr

        _house = (
            community_models.House.objects.filter(deleted=False)
            .filter(uuid=house_uuid)
            .select_for_update()
            .first()
        )
        if not _house:
            raise app_except.ObjectNotExist

        _community = (
            community_models.Community.objects.filter(deleted=False)
            .filter(uuid=_house.community_uuid)
            .first()
        )
        if not _community:
            raise app_except.ObjectNotExist

        order_title = f"{_community.community_name}社区-物业管理"

        _house_bill = community_models.HouseBill(
            days=days,
            house_uuid=house_uuid,
            start_at=_house.paid_at,
            pay_user_openid=wxuser.openid,
        )
        _house_bill_s = community_models.batch_create_house_bill([_house_bill])
        _up_house_bill: community_models.HouseBill = _house_bill_s[0]

        total_fee = int(_up_house_bill.price)

        data, out_trade_no = wx_pay.unifiedorder(
            request,
            total_fee=total_fee,
            order_title=order_title,
            house_uuid=house_uuid,
            house_bill_uuid=_house_bill.uuid,
            call_back_url="/api/wx_user/wxuser/pay_callback",
        )

        payment = data.get("respdata").get("payment")

        if not out_trade_no:
            raise app_except.ValueErr

        if not payment:
            raise app_except.ValueErr

        return json_response_utf8_ok(
            {"code": 0, "payment": payment, "out_trade_no": out_trade_no}
        )


@method_decorator(wx_auth.try_and_need_wx_login_decorators, name="dispatch")
class WxUserPayCallBackView(View):
    @transaction.atomic
    def post(self, request: HttpRequest, *args, **kwargs):
        # https://developers.weixin.qq.com/miniprogram/dev/wxcloudrun/src/development/pay/callback/

        o: dict = json.loads(request.body)
        logger.debug(f"pay_callback request.body: {o}")

        resultCode = o.get("resultCode", "").strip()
        if resultCode != "SUCCESS":
            raise app_except.ValueErr

        openid = o.get("subOpenid", None)
        if not openid:
            raise app_except.NullErr

        total_fee = int(o.get("totalFee", None))
        if total_fee < 1:
            raise app_except.ParamsErr

        out_trade_no = o.get("outTradeNo", None)
        if not out_trade_no:
            raise app_except.NullErr

        uro = (
            models.WxUserRechargeOrder.objects.filter(deleted=False)
            .filter(out_trade_no=out_trade_no)
            .select_for_update()
            .filter(state=models.WxUserRechargeOrder.OrderState.WAIT.value)
            .first()
        )
        if not uro:
            raise app_except.ObjectNotExist

        uro.state = models.WxUserRechargeOrder.OrderState.SUCCESS.value
        uro.pay_at = timezone.now()
        uro.save()

        house_bill_uuid = uro.house_bill_uuid
        _house_bill = (
            community_models.HouseBill.objects.filter(deleted=False)
            .filter(uuid=house_bill_uuid)
            .select_for_update()
            .first()
        )
        if not _house_bill:
            raise app_except.ObjectNotExist

        _house_bill.state = community_models.HouseBill.BillState.PAID.value
        _house_bill.pay_user_openid = (uro.openid,)
        _house_bill.save()

        _house = (
            community_models.House.objects.filter(deleted=False)
            .filter(uuid=_house_bill.house_uuid)
            .select_for_update()
            .first()
        )
        if not _house:
            raise app_except.ObjectNotExist

        _house.paid_at += timedelta(days=int(_house_bill.days))
        _house.save()

        data = {"errcode": 0, "errmsg": ""}
        return json_response_utf8_ok(
            data,
        )
